
package com.hulk.ratel.web.interceptor;


import cn.hutool.core.util.EscapeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;

import com.hulk.ratel.common.constant.enums.BussErrCode;
import com.hulk.ratel.common.exception.BaseRteException;
import com.hulk.ratel.common.exception.SecurityRteException;
import com.hulk.ratel.common.exception.WebRteException;
import com.hulk.ratel.common.security.ProvideSecurityWarper;
import com.hulk.ratel.common.util.DateUtil;
import com.hulk.ratel.common.util.IDWorkerRadix;

import com.hulk.ratel.manage.commoncache.PartnerKeyCache;
import com.hulk.ratel.manage.commoncache.SecurityPartnerCache;
import com.hulk.ratel.persistence.entity.PartnerKey;
import lombok.extern.slf4j.Slf4j;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
import java.util.Optional;

/**
 * @author hulk
 * @version Create on: 2017年4月12日 下午2:29:11 Class description
 * @E-mail:29572320@qq.com
 */

@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private SecurityPartnerCache securityPartnerCache;

    @Autowired
    private PartnerKeyCache partnerKeyCache;

     @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //本交易全局唯一流水
        String workId = IDWorkerRadix.getId();
        //日切日志
        String intCutDate = DateUtil.getDateyyyyMMdd();

        //心跳检查路径
        String URI = request.getRequestURI().trim();
        List<String> array = Splitter.on('/').trimResults().omitEmptyStrings().splitToList(URI);
        if (URI.contains("heartbeat")) {
            return super.preHandle(request, response, handler);
        }
        if (!URI.contains("api")) {
            return super.preHandle(request, response, handler);
        }
        //交易代码
        String txnCodeURISuffix = array.get(array.size() - 1);

        //AES加密的报文
        String encryptData = request.getParameter("encryptData");
        if (Strings.isNullOrEmpty(encryptData)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "encryptData:" + BussErrCode.E_100001.getErrDesc());
        }
        //RSA公钥加密的AES秘钥
        String encryptKey = request.getParameter("encryptKey");
        if (Strings.isNullOrEmpty(encryptKey)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "encryptKey:" + BussErrCode.E_100001.getErrDesc());
        }
        //报文的HA256签名
        String signature = request.getParameter("signature");
        if (Strings.isNullOrEmpty(signature)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "signature:" + BussErrCode.E_100001.getErrDesc());
        }
        //交易唯一流水
        String traceId = request.getParameter("traceId");
        if (Strings.isNullOrEmpty(traceId)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "traceId:" + BussErrCode.E_100001.getErrDesc());
        }
        //合作方号
        String partnerNo = request.getParameter("partnerNo");
        if (Strings.isNullOrEmpty(partnerNo)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "partnerNo:" + BussErrCode.E_100001.getErrDesc());
        }

        log.info("接受合作者报文内容: URI:{},txnCode:{}, encryptData:{},encryptKey:{},signature:{},partnerNo:{},traceId:{},生成平台号：workId:{}",
                new Object[]{URI, txnCodeURISuffix, encryptData, encryptKey, signature, partnerNo, traceId, workId});
        request.setAttribute("workId", workId);
        request.setAttribute("txnCode", txnCodeURISuffix);
        request.setAttribute("partnerNo", partnerNo);
        request.setAttribute("traceId", traceId);
        request.setAttribute("intCutDate", intCutDate);

        String plainText;
        String partnerAESKey;
        boolean flag;
        try {
            //合作方密钥查询
            PartnerKey partnerKey = Optional.ofNullable(partnerKeyCache.findPartnerKey(partnerNo))
                    .orElseThrow(()->new SecurityRteException(workId,BussErrCode.E_100002));
            String partnerPublicKeyStr = partnerKey.getPartnerPublickey();
            String platformPrivateKeyStr = partnerKey.getPlatformPrivatekey();
            //String partnerPublicKeySuffix = partnerPublicKeyStr.substring(partnerPublicKeyStr.lastIndexOf(".") + 1);
            String partnerPublicKeySuffix = partnerKey.getPartnerPublickeySuffix();
            //String platformPrivateKeySuffix = platformPrivateKeyStr.substring(platformPrivateKeyStr.lastIndexOf(".") + 1);
            String platformPrivateKeySuffix =partnerKey.getPlatformPrivatekeySuffix();
            String keypathType =  partnerKey.getKeypathType();
            //获取合作方公钥
            PublicKey publicKeyPartner = Optional.ofNullable(securityPartnerCache.findPublicKey(partnerNo, partnerPublicKeyStr, partnerPublicKeySuffix, keypathType))
                    .orElseThrow(() -> new SecurityRteException(workId, BussErrCode.E_100010));
            //获取平台私钥
            PrivateKey privateKeyPlatform = Optional.ofNullable(securityPartnerCache.findPrivateKey(partnerNo,platformPrivateKeyStr,platformPrivateKeySuffix, keypathType))
                    .orElseThrow(() -> new SecurityRteException(workId, BussErrCode.E_100020));
            //获取合作方AES秘钥
            partnerAESKey = ProvideSecurityWarper.getPartnerAESKeyByPlatformPrivateKey(encryptKey,
                    privateKeyPlatform);
            //解密合作方报文得到明文
            plainText = ProvideSecurityWarper.getPlainTextByAESKey(encryptData, partnerAESKey);
            //验证合作方报文签名
            flag = ProvideSecurityWarper.checkSignB64PlainTextByPartnerPublicKey(plainText, signature,
                    publicKeyPartner);
        } catch (BaseRteException be) {
            throw new SecurityRteException(workId, be.getErrorCode(), be.getErrorMessage());
        } catch (Exception e) {
            throw new SecurityRteException(workId, BussErrCode.E_100004);
        }
        if (Strings.isNullOrEmpty(partnerAESKey) | (16 != partnerAESKey.trim().length())) {
            throw new SecurityRteException(workId, BussErrCode.E_100015);
        }
        //plainText = plainText.replace("&quot;", "\"");
        //plainText = StringEscapeUtils.unescapeHtml4(plainText);
        plainText =  EscapeUtil.unescapeHtml4(plainText);
        log.info("plainText unescapeHtml4:{}", plainText);
        JsonNode node =   objectMapper.readTree(plainText);
        node = node.get("head");
        String traceIdHead = node.get("traceId").textValue().trim();
        String partnerNoHead = node.get("partnerNo").textValue().trim();
        String txnCodeHead = node.get("txnCode").textValue().trim();
        if (Strings.isNullOrEmpty(traceIdHead) | Strings.isNullOrEmpty(partnerNoHead) | Strings.isNullOrEmpty(txnCodeHead)) {
            throw new WebRteException(workId, BussErrCode.E_100001);
        }
        if (!Objects.equal(traceIdHead, traceId)) {
            throw new WebRteException(workId, BussErrCode.E_100001.getErrCode(), "traceId和traceIdHead不相等");
        }
        if (!Objects.equal(partnerNoHead, partnerNo)) {
            throw new WebRteException(workId, BussErrCode.E_100002);
        }
        if (!Objects.equal(txnCodeHead, txnCodeURISuffix)) {
            throw new WebRteException(workId, BussErrCode.E_100021);
        }
        //报文签名不正确
        if (!flag) {
            throw new SecurityRteException(workId, BussErrCode.E_100003);
        }

        request.setAttribute("plainText", plainText);
        request.setAttribute("partnerAESKey", partnerAESKey);
        request.setAttribute("requestHead", objectMapper.readTree(plainText).path("head").toString());

        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        log.debug("CommunicationSecurityInterceptor.postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        log.debug("CommunicationSecurityInterceptor.afterCompletion", ex);
    }
}
